{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6b3b79d7",
   "metadata": {},
   "source": [
    "# 第二章 插件基础"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3827cc9c",
   "metadata": {},
   "source": [
    "## 一、插件框架和打包"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f41ae24f",
   "metadata": {},
   "source": [
    "根据前两章的内容建立新插件文件，获得如下初始代码并稍微修改："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "68bfa598",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 基本信息\n",
    "bl_info = {\n",
    "    # 插件名称\n",
    "    \"name\" : \"test\",\n",
    "    # 作者\n",
    "    \"author\" : \"sky\",\n",
    "    # 简单介绍\n",
    "    \"description\" : \"这是个测试插件\",\n",
    "    # blender 有些地方必须是零 如 2, 90, 0\n",
    "    \"blender\" : (2, 80, 0),\n",
    "    # 插件版本\n",
    "    \"version\" : (0, 0, 1),\n",
    "    # 工作区\n",
    "    \"location\" : \"\",\n",
    "    # 提供警告信息  只要写了内容 即为开启警告\n",
    "    \"warning\" : \"不稳定\",\n",
    "    # 插件分类  可选用官方提供的分类 也可自定义\n",
    "    \"category\" : \"TEST\"\n",
    "    # 文档连接地址\n",
    "    \"doc_url\":\"https://www.bilibili.com\",\n",
    "    # bug报告地址\n",
    "    \"tracker_url\":\"https://www.github.com\"\n",
    "}\n",
    "\n",
    "#在blender SDK加载界面选择加载时的动作\n",
    "def register():\n",
    "    print(\"hello_blender\") # 这里最好不用中文\n",
    "#在blender SDK 界面取消加载时的动作\n",
    "def unregister():\n",
    "    print(\"bye_blender\")  # 这里最好不用中文\n",
    " "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e42d4cd1",
   "metadata": {},
   "source": [
    "注：更改插件信息后 需要重启blender才会刷新插件内容"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6fa45385",
   "metadata": {},
   "source": [
    "#### 插件卸载问题 \n",
    "即在Blender界面内Remove该插件会将原本文件一并删除  \n",
    "此时我们采取进入插件安装目录addon   \n",
    "将插件快捷方式删除即可"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1494a61",
   "metadata": {},
   "source": [
    "### 插件的打包发布\n",
    "将插件文件夹压缩为zip格式    \n",
    "进入Blender Add-ons Install插件 找到该zip安装即可"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "487ad45a",
   "metadata": {},
   "source": [
    "## 二、Operators 详解"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39e4cb87",
   "metadata": {},
   "source": [
    "Operators 所有的操作指令，如按钮，建模操作，创建、删除模型  \n",
    "首先我们回顾一下上期内容\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c4dfa9bc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 移动\n",
    "bpy.ops.outliner\n",
    ".item_activate(extend=False, deselect_all=True)\n",
    "# 删除选中的物体\n",
    "bpy.ops.object.delete(use_global=False)\n",
    "# 在游标处创建UV球\n",
    "bpy.ops.mesh\n",
    ".primitive_uv_sphere_add\n",
    "(radius=1, enter_editmode=False, align='WORLD',\n",
    " location=(0, 0, 0), scale=(1, 1, 1))\n",
    "# 编辑模式和物体模式切换\n",
    "bpy.ops.object.editmode_toggle()\n",
    "# 网格细分动作\n",
    "bpy.ops.mesh.subdivide()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "97f74be0",
   "metadata": {},
   "source": [
    "下面是新内容"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7fc639a7",
   "metadata": {},
   "source": [
    "### 操作 "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "879647fe",
   "metadata": {},
   "source": [
    "1 invoke  初始化操作   \n",
    "2 execute 执行操作  \n",
    "3 modal 一直运行 如快捷键 G R S 的实现  \n",
    "4 poll 名义检测 用来检测状态  检测是否在物体模式编辑模式  \n",
    "5 invoke  execute modal 必须返回固定值：  \n",
    "- 'RUNNING_MODAL'   : 运行modal\n",
    "- 'CANCELLED'  : 取消操作（右键）：无undo\n",
    "- 'FINISHED'  : 确定操作（左键）：有undo\n",
    "- 'PASS_THROUGH' : 啥都不做 跳过该事件\n",
    "- 'INTERFACE' ： 悬置但是不执行"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ec3351a0",
   "metadata": {},
   "outputs": [],
   "source": [
    "#例子\n",
    "class myTestOpt(bpy.types.Operator):\n",
    "    bl_idname = \"sky\"\n",
    "    # 可在blender内用F3直接根据下列名字查找\n",
    "    bl_label = \"Hello\"\n",
    "    \n",
    "    def execute(self,context):\n",
    "        print(\"hello ops\")\n",
    "        # 下面会弹出一个错误提示窗口  也可以填入 INFO , WARNNING   其他具体见API\n",
    "        self.report({'ERROR'},\"hello ops\")\n",
    "        return {\"FINISHED\"}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "55e53b2b",
   "metadata": {},
   "source": [
    "完成后需要在blender内注册 和卸载"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5c57cdf2",
   "metadata": {},
   "outputs": [],
   "source": [
    "def register():\n",
    "    bpy.utils.register_class(myTestOpt)\n",
    "def unregister():\n",
    "    bpy.utils.unregister_class(myTestOpt) "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "afff88ee",
   "metadata": {},
   "source": [
    "## 三、invoke 和 report 和 属性定义"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e0bb4ae",
   "metadata": {},
   "source": [
    "实用RNA属性对Operator进行定义 才能在界面里找到该属性   \n",
    "即 操作后 会弹出界面  界面内会出现定义的几个RNA属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c0ee6e7c",
   "metadata": {},
   "outputs": [],
   "source": [
    "mFloat:bpy.props.FloatProperty(name=\"float\",default = 1.0)\n",
    "mStr:bpy.props.StringProperty(name=\"string\",default = \"hello\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f5254cb",
   "metadata": {},
   "source": [
    "invoke定义"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0a0e24b5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义invoke  \n",
    "# event为对键盘检测 ， 在ops执行时 对快捷键检测\n",
    "def invoke (self,context,event):\n",
    "    if event.type == \"LEFTMOUSE\":\n",
    "        self.report({\"WARNNING\",\"left mouse\"})\n",
    "    if event.type == \"RIGHTMOUSE\":\n",
    "        self.report({\"INFO\",\"right mouse\"})\n",
    "        \n",
    "    self.mfloat = 2.0\n",
    "    self.mStr = \"hellox\"\n",
    "    \n",
    "    #return {'FINISHED'} 此处可以直接结束\n",
    "    return self.execute(context)  #此处调用execute函数  即为回调函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "15a62d52",
   "metadata": {},
   "outputs": [],
   "source": [
    "下面是完整代码："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1cb7fd02",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 基本信息\n",
    "\n",
    "bl_info = {\n",
    "    \"name\" : \"test\",\n",
    "    \"author\" : \"sky\",\n",
    "    \"description\" : \"\",\n",
    "    \"blender\" : (2, 80, 0),\n",
    "    \"version\" : (0, 0, 1),\n",
    "    \"location\" : \"\",\n",
    "    \"warning\" : \"\",\n",
    "    \"category\" : \"Generic\"\n",
    "}\n",
    "\n",
    "import bpy\n",
    "\n",
    "def register():\n",
    "    bpy.utils.register_class(myTestOpt)\n",
    "def unregister():\n",
    "    bpy.utils.unregister_class(myTestOpt)\n",
    "    \n",
    "\n",
    "class myTestOpt(bpy.types.Operator):\n",
    "    bl_idname = \"sky.invoke_report_test\"\n",
    "    # 可在blender内用F3直接根据下列名字查找\n",
    "    bl_label = \"Hello\"\n",
    "    \n",
    "    #ops属性定义\n",
    "    mFloat:bpy.props.FloatProperty (name=\"float\" ,default = 1.0    )\n",
    "    mStr:  bpy.props.StringProperty(name=\"string\",default = \"hello\")\n",
    "    \n",
    "    def execute(self,context):\n",
    "        print(\"execute start\")\n",
    "        # 下面会弹出一个错误提示窗口  也可以填入 INFO , WARNNING   其他具体见API\n",
    "        self.report({'ERROR'},\"show error\")\n",
    "        \n",
    "        print(\"mfloat is : %d ,str is %s\" %(self.mFloat,self.mStr))\n",
    "        \n",
    "        return {\"FINISHED\"}\n",
    "    \n",
    "    \n",
    "    def invoke (self,context,event):\n",
    "        if event.type == \"LEFTMOUSE\":\n",
    "            self.report({\"WARNING\"},\"left mouse\")\n",
    "        if event.type == \"RIGHTMOUSE\":\n",
    "            self.report({\"INFO\"},\"right mouse\" )\n",
    "\n",
    "        self.mfloat = 2.0\n",
    "        self.mStr = \"hellox\"\n",
    "\n",
    "        return self.execute(context)#此处调用execute函数  即为回调函数"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb54222f",
   "metadata": {},
   "source": [
    "## 四、poll检测 检测当前处于的激活区域"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "264149a5",
   "metadata": {},
   "source": [
    "作用1：区域检测   \n",
    "检测 C.area.ui_type 的结果  \n",
    "如果在blender内打开F3操作的面板位置为poll内允许的面板信息 则能够看见当前插件  \n",
    "例如：  \n",
    "poll 规定在 C.area.ui_type 为 \"VIEW_3D\" （3D操作面板） 的时候  用F3才能找到当前这个插件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e483485c",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1c4533ad",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "bl_info = {\n",
    "    \"name\" : \"test\",\n",
    "    \"author\" : \"sky\",\n",
    "    \"description\" : \"\",\n",
    "    \"blender\" : (2, 80, 0),\n",
    "    \"version\" : (0, 0, 1),\n",
    "    \"location\" : \"\",\n",
    "    \"warning\" : \"\",\n",
    "    \"category\" : \"Generic\"\n",
    "}\n",
    "\n",
    "import bpy\n",
    "\n",
    "def register():\n",
    "    bpy.utils.register_class(myTestOpt)\n",
    "def unregister():\n",
    "    bpy.utils.unregister_class(myTestOpt)\n",
    "    \n",
    "\n",
    "class myTestOpt(bpy.types.Operator):\n",
    "    bl_idname = \"sky.poll_test\"\n",
    "    # 可在blender内用F3直接根据下列名字查找\n",
    "    bl_label = \"Hello\"\n",
    "    \n",
    "    #ops属性定义\n",
    "    mFloat:bpy.props.FloatProperty (name=\"float\" ,default = 1.0    )\n",
    "    mStr:  bpy.props.StringProperty(name=\"string\",default = \"hello\")\n",
    "    \n",
    "    #可在此设定插件的可以工作的区域\n",
    "    @classmethod\n",
    "    def poll(cls,context):\n",
    "        if context.area.ui_type == \"VIEW_3D\":\n",
    "            return True\n",
    "        else:\n",
    "            return False\n",
    "    \n",
    "    def execute(self,context):\n",
    "        \n",
    "        print('execute')\n",
    "        \n",
    "        return {\"FINISHED\"}\n",
    "    \n",
    "    \n",
    "    def invoke (self,context,event):\n",
    "        \n",
    "        #可在此设定插件的可以工作的区域  与上述的效果类似\n",
    "        if self.poll():\n",
    "            ...\n",
    " \n",
    "        self.mfloat = 2.0\n",
    "        self.mStr = \"hellox\"\n",
    "    \n",
    "        return self.execute(context)#此处调用execute函数  即为回调函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bd6cbe9a",
   "metadata": {},
   "outputs": [],
   "source": [
    "#在命令行可使用定义好的ops\n",
    "bpy.ops.sky.poll_test(mFloat=1, mStr=\"hello\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "811f9c8a",
   "metadata": {},
   "outputs": [],
   "source": [
    "#在命令行可查看poll\n",
    "bpy.ops.sky.poll_test.poll()\n",
    "#False 说明当前面板 被poll判断为插件不生效 "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "198bbb5c",
   "metadata": {},
   "source": [
    "## 五、modal 和 综合案例"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fe573e0d",
   "metadata": {},
   "source": [
    "代码从invoke进入modal后就一直在modal内循环执行，直到符合终止返回指令   \n",
    "用于实时监控捕捉操作动作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1142aecb",
   "metadata": {},
   "outputs": [],
   "source": [
    "#代码1  ： 说明modal的循环机制\n",
    "bl_info = {\n",
    "    \"name\" : \"test\",\n",
    "    \"author\" : \"sky\",\n",
    "    \"description\" : \"\",\n",
    "    \"blender\" : (2, 90, 0),\n",
    "    \"version\" : (0, 0, 1),\n",
    "    \"location\" : \"\",\n",
    "    \"warning\" : \"\",\n",
    "    \"category\" : \"Generic\"\n",
    "}\n",
    "\n",
    "import bpy\n",
    " \n",
    "def register():\n",
    "    bpy.utils.register_class(MoveCubeOperator)\n",
    "def unregister():\n",
    "    bpy.utils.unregister_class(MoveCubeOperator)\n",
    "    \n",
    "\n",
    "class MoveCubeOperator(bpy.types.Operator):\n",
    "    bl_idname = \"sky.movecube\"\n",
    "    bl_label = \"move cube\"\n",
    "    #bl_description = \"Description that shows in blender tooltips\"\n",
    "    bl_options = {\"REGISTER\"}\n",
    "\n",
    "    @classmethod\n",
    "    def poll(cls, context):\n",
    "        return True\n",
    "\n",
    "    def execute(self, context): \n",
    "        return {\"FINISHED\"}\n",
    "\n",
    "    def invoke(self,context,event): \n",
    "\n",
    "        #此处模板给出的是个文件选择器  这里我们不用\n",
    "        #context.window_manager.fileselect_add(self)  \n",
    "\n",
    "        #启动modal\n",
    "        context.window_manager.modal_handler_add(self)\n",
    "        return {'RUNNING_MODAL'}  #此处进入modal\n",
    "\n",
    "\n",
    "    def modal(self , context , event):\n",
    "        \n",
    "        print('modal')\n",
    "\n",
    "        #由于进入modal程序会在此死循环  故需要对按键检测 获得终止并找到退出口\n",
    "        if event.type == \"LEFTMOUSE\":\n",
    "            #self.report({\"WARNING\"},\"left mouse\")\n",
    "            #self.report({\"INFO\"},\"left mouse\" )\n",
    "            print(\"ok\")\n",
    "            return {'FINISHED'}\n",
    "\n",
    "        if event.type in {\"RIGHTMOUSE\",\"ESC\"}:\n",
    "            print(\"cancelled\")\n",
    "            return {'FINISHED'} \n",
    "\n",
    "        return{'RUNNING_MODAL'}  #此处进入modal  并在modal内部循环\n",
    " "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5fd18b85",
   "metadata": {},
   "source": [
    "### 下面是一个移动方块用的综合实例  \n",
    "\n",
    "这里使用poll确定在所有面板均可执行插件   \n",
    "invoke进入插件   开启modal循环   并在modal内不断执行execute\n",
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c8b27904",
   "metadata": {},
   "outputs": [],
   "source": [
    "  \n",
    "bl_info = {\n",
    "    \"name\" : \"test\",\n",
    "    \"author\" : \"sky\",\n",
    "    \"description\" : \"\",\n",
    "    \"blender\" : (2, 90, 0),\n",
    "    \"version\" : (0, 0, 1),\n",
    "    \"location\" : \"\",\n",
    "    \"warning\" : \"\",\n",
    "    \"category\" : \"Generic\"\n",
    "}\n",
    "\n",
    "import bpy\n",
    " \n",
    "def register():\n",
    "    bpy.utils.register_class(MoveCubeOperator)\n",
    "def unregister():\n",
    "    bpy.utils.unregister_class(MoveCubeOperator)\n",
    "    \n",
    "\n",
    "class MoveCubeOperator(bpy.types.Operator):\n",
    "    bl_idname = \"sky.movecube\"\n",
    "    bl_label = \"move cube\"\n",
    "    #bl_description = \"Description that shows in blender tooltips\"\n",
    "    bl_options = {\"REGISTER\"}\n",
    "\n",
    "    # mouse location 定义鼠标位置信息\n",
    "    mouseRX:bpy.props.FloatProperty(name=\"float\", default=0)\n",
    "    mouseRY:bpy.props.FloatProperty(name=\"float\", default=0)\n",
    "    mousePX:bpy.props.FloatProperty(name=\"float\", default=0)\n",
    "    mousePY:bpy.props.FloatProperty(name=\"float\", default=0)\n",
    "    mouseX:bpy.props.FloatProperty(name=\"float\", default=0)\n",
    "    mouseY:bpy.props.FloatProperty(name=\"float\", default=0)\n",
    "\n",
    "    #原始位置变量\n",
    "    defX = 0.0\n",
    "    defY = 0.0\n",
    " \n",
    "    @classmethod\n",
    "    def poll(cls, context):\n",
    "        return True\n",
    "\n",
    "    def execute(self, context): \n",
    "\n",
    "        # 将获得的 x y  影响修改 实体模型obj的位置\n",
    "        obj = context.object\n",
    "        obj.location[0]= self.mouseX\n",
    "        obj.location[1]= self.mouseY\n",
    "\n",
    "        #return {\"FINISHED\"}\n",
    "\n",
    "    def invoke(self,context,event): \n",
    "\n",
    "        #获得需要移动物体的原始位置\n",
    "        self.defX = context.object.location[0]\n",
    "        self.defY = context.object.location[1]\n",
    "\n",
    "        #此处模板给出的是个文件选择器  这里我们不用\n",
    "        #context.window_manager.fileselect_add(self)  \n",
    "\n",
    "        #启动modal\n",
    "        context.window_manager.modal_handler_add(self)\n",
    "        return {'RUNNING_MODAL'}  #此处进入modal\n",
    "\n",
    "\n",
    "    def modal(self , context , event):\n",
    "\n",
    "        print('modal') \n",
    "\n",
    "        #实时获得鼠标的信息\n",
    "        self.mouseRX = event.mouse_region_x             /100\n",
    "        self.mouseRY = event.mouse_region_y             /100\n",
    "        self.mousePX = event.mouse_prev_x               /100\n",
    "        self.mousePY = event.mouse_prev_y               /100\n",
    "        self.mouseX = event.mouse_x                     /100\n",
    "        self.mouseY = event.mouse_y                     /100\n",
    " \n",
    "        print('px is %d , py is %d' % (self.mousePX , self.mousePY))\n",
    "        print('x is %d , y is %d' % (self.mouseX , self.mouseY))\n",
    "        print('rx is %d , ry is %d' % (self.mouseRX , self.mouseRY))\n",
    "\n",
    "        #此处启用execute  execute内有改变物体位置的动作\n",
    "        self.execute(context)\n",
    "        \n",
    "        #由于进入modal程序会在此死循环  故需要对按键检测 获得终止并找到退出口\n",
    "        if event.type == \"LEFTMOUSE\":\n",
    "            #self.report({\"WARNING\"},\"left mouse\")\n",
    "            #self.report({\"INFO\"},\"left mouse\" )\n",
    "            print(\"ok\")\n",
    "            return {'FINISHED'}\n",
    "\n",
    "        if event.type in {\"RIGHTMOUSE\",\"ESC\"}:\n",
    "            #进行右键或esc退出时 将物体还原到初始位置 \n",
    "            context.object.location[0] =self.defX \n",
    "            context.object.location[1] =self.defY \n",
    "            print(\"cancelled\")\n",
    "            return {'FINISHED'} \n",
    "\n",
    "        return{'RUNNING_MODAL'}  #此处进入modal  并在modal内部循环\n",
    "\n",
    " "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4dd62ff9",
   "metadata": {},
   "source": [
    "## 六、popu窗口"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4e186f4f",
   "metadata": {},
   "source": [
    "当完成物体的操作后，弹出窗口，该动作可用popu实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fedb0fd5",
   "metadata": {},
   "outputs": [],
   "source": [
    "#需要如下内容   UNDO  即为打开UNDO面板\n",
    "bl_options = {\"REGISTER\",\"UNDO\"}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8c3c8570",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "bl_info = {\n",
    "    \"name\" : \"test\",\n",
    "    \"author\" : \"sky\",\n",
    "    \"description\" : \"\",\n",
    "    \"blender\" : (2, 90, 0),\n",
    "    \"version\" : (0, 0, 1),\n",
    "    \"location\" : \"\",\n",
    "    \"warning\" : \"\",\n",
    "    \"category\" : \"Generic\"\n",
    "}\n",
    "\n",
    "import bpy\n",
    " \n",
    "def register():\n",
    "    bpy.utils.register_class(MoveCubeOperator)\n",
    "def unregister():\n",
    "    bpy.utils.unregister_class(MoveCubeOperator)\n",
    "    \n",
    "\n",
    "class MoveCubeOperator(bpy.types.Operator):\n",
    "    bl_idname = \"sky.movecube\"\n",
    "    bl_label = \"move cube\" \n",
    "    #用于在modal循环完成后继续弹出窗口\n",
    "    bl_options = {\"REGISTER\",\"UNDO\"}\n",
    "\n",
    "    # mouse location 定义鼠标位置信息\n",
    "    mouseRX:bpy.props.FloatProperty(name=\"float\", default=0)\n",
    "    mouseRY:bpy.props.FloatProperty(name=\"float\", default=0)\n",
    "    mousePX:bpy.props.FloatProperty(name=\"float\", default=0)\n",
    "    mousePY:bpy.props.FloatProperty(name=\"float\", default=0)\n",
    "    mouseX:bpy.props.FloatProperty(name=\"LocationX\", default=0,description=\"X轴位置\")\n",
    "    mouseY:bpy.props.FloatProperty(name=\"LocationY\", default=0,description=\"Y轴位置\")\n",
    "\n",
    "    #原始位置变量\n",
    "    defX = 0.0\n",
    "    defY = 0.0\n",
    "    #定义一个结束标识，用于检测在modal循环执行execute的时候是否应该结束循环\n",
    "    isOK = False\n",
    " \n",
    "    @classmethod\n",
    "    def poll(cls, context):\n",
    "        return True\n",
    "\n",
    "    def execute(self, context): \n",
    "\n",
    "        # 将获得的 x y  影响修改 实体模型obj的位置\n",
    "        obj = context.object\n",
    "        obj.location[0]= self.mouseX\n",
    "        obj.location[1]= self.mouseY\n",
    "        \n",
    "        #控制是否结束ops\n",
    "        if self.isOK :\n",
    "            return {\"FINISHED\"}\n",
    "\n",
    "    def invoke(self,context,event): \n",
    "\n",
    "        #获得需要移动物体的原始位置\n",
    "        self.defX = context.object.location[0]\n",
    "        self.defY = context.object.location[1]\n",
    "\n",
    "        #此处模板给出的是个文件选择器  这里我们不用\n",
    "        #context.window_manager.fileselect_add(self)  \n",
    "\n",
    "        #启动modal\n",
    "        context.window_manager.modal_handler_add(self)\n",
    "        return {'RUNNING_MODAL'}  #此处进入modal\n",
    "\n",
    "\n",
    "    def modal(self , context , event):\n",
    "\n",
    "        print('modal') \n",
    "\n",
    "        #实时获得鼠标的信息\n",
    "        self.mouseRX = event.mouse_region_x             /100\n",
    "        self.mouseRY = event.mouse_region_y             /100\n",
    "        self.mousePX = event.mouse_prev_x               /100\n",
    "        self.mousePY = event.mouse_prev_y               /100\n",
    "        self.mouseX = event.mouse_x                     /100\n",
    "        self.mouseY = event.mouse_y                     /100\n",
    " \n",
    "        print('px is %d , py is %d' % (self.mousePX , self.mousePY))\n",
    "        print('x is %d , y is %d' % (self.mouseX , self.mouseY))\n",
    "        print('rx is %d , ry is %d' % (self.mouseRX , self.mouseRY))\n",
    "\n",
    "        #此处启用execute  execute内有改变物体位置的动作\n",
    "        self.execute(context)\n",
    "        \n",
    "        #由于进入modal程序会在此死循环  故需要对按键检测 获得终止并找到退出口\n",
    "        if event.type == \"LEFTMOUSE\": \n",
    "            self.isOK = True\n",
    "            print(\"ok\")\n",
    "            return {'FINISHED'}\n",
    "\n",
    "        if event.type in {\"RIGHTMOUSE\",\"ESC\"}:\n",
    "            #进行右键或esc退出时 将物体还原到初始位置 \n",
    "            context.object.location[0] =self.defX \n",
    "            context.object.location[1] =self.defY \n",
    "            print(\"cancelled\")\n",
    "            return {'FINISHED'} \n",
    "\n",
    "        return{'RUNNING_MODAL'}  #此处进入modal  并在modal内部循环\n",
    " "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ff8910f",
   "metadata": {},
   "source": [
    "## 七、poll强化"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "67e8b13a",
   "metadata": {},
   "source": [
    "我们只希望上述移动插件只能在三维操作界面 且 物体模式下 才能使用   \n",
    "故此处需要编辑poll控制插件可用的条件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eac121a2",
   "metadata": {},
   "outputs": [],
   "source": [
    "@classmethod\n",
    "def poll(cls, context):\n",
    "    \n",
    "    #是否在3D视图  #是否有选择的激活的物体  # 是否处在物体模式下\n",
    "    if C.area.ui_type == \"VIEW_3D\" \n",
    "    and  C.active_object is not None\n",
    "    and C.object.mode == \"OBJECT\"     :\n",
    "        return True \n",
    "    else:\n",
    "        return False\n",
    "    return True"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15981380",
   "metadata": {},
   "source": [
    "# 八、operator参数的传递和调用 "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "674f0802",
   "metadata": {},
   "source": [
    "包含如下：\n",
    "1. override_context 超驰（覆写）context \n",
    "2. execution_context\n",
    "3. undo\n",
    "\n",
    "override  重写  override 重载"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0dcb1725",
   "metadata": {},
   "source": [
    "override_context 对context覆写  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ae1aa749",
   "metadata": {},
   "outputs": [],
   "source": [
    "#由于context只读  故先拷贝\n",
    "override = bpy.context.copy()\n",
    "#对context修改 将场景内所有物体作为选中   此时context内的几十个属性都能修改了\n",
    "override['selected_objects'] = list(bpy.context.scene.objects)\n",
    "#选中删除的动作应用到新的context上  即删除所有物体\n",
    "bpy.ops.object.delete(override) "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23defcc0",
   "metadata": {},
   "source": [
    "execution_context 对context执行   \n",
    "常用 EXEC_DEFAULT 调用execute   \n",
    "常用INVOKE_DEFAULT  调用invoke   "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "426681b2",
   "metadata": {},
   "outputs": [],
   "source": [
    " \n",
    "bl_info = {\n",
    "    \"name\" : \"test\",\n",
    "    \"author\" : \"sky\",\n",
    "    \"description\" : \"\",\n",
    "    \"blender\" : (2, 90, 0),\n",
    "    \"version\" : (0, 0, 1),\n",
    "    \"location\" : \"\",\n",
    "    \"warning\" : \"\",\n",
    "    \"category\" : \"Generic\"\n",
    "}\n",
    "\n",
    "import bpy\n",
    " \n",
    "def register():\n",
    "    bpy.utils.register_class(ContextExecOperator)\n",
    "def unregister():\n",
    "    bpy.utils.unregister_class(ContextExecOperator)\n",
    " \n",
    "class ContextExecOperator(bpy.types.Operator):\n",
    "    bl_idname = \"sky.context_exec\"\n",
    "    bl_label = \"context_exec\"  \n",
    "    bl_options = {\"REGISTER\" }\n",
    "\n",
    "    #classmethod\n",
    "    def poll(cls,context):\n",
    "        return True\n",
    "    \n",
    "    def execute(self,context):\n",
    "        print(\"execute\")\n",
    "        return {'FINISHED'}\n",
    "    \n",
    "    def invoke (self,context,event):\n",
    "        print(\"invoke\")\n",
    "        return self.execute(context)\n",
    "      "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4c980935",
   "metadata": {},
   "source": [
    "上述代码在blender运行"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5ed35e5b",
   "metadata": {},
   "outputs": [],
   "source": [
    "bpy.ops.sky.context_exec('INVOKE_DEFAULT')\n",
    "#返回 invoke execute {'FINISHED'}\n",
    "\n",
    "bpy.ops.sky.context_exec('EXEC_DEFAULT')\n",
    "#返回  execute {'FINISHED'}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "abb284c1",
   "metadata": {},
   "source": [
    "## 九、自动属性和operator内context的调用"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "275216ee",
   "metadata": {},
   "source": [
    "operator只会识别RNA属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c90c66f2",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "bl_info = {\n",
    "    \"name\" : \"test\",\n",
    "    \"author\" : \"sky\",\n",
    "    \"description\" : \"\",\n",
    "    \"blender\" : (2, 90, 0),\n",
    "    \"version\" : (0, 0, 1),\n",
    "    \"location\" : \"\",\n",
    "    \"warning\" : \"\",\n",
    "    \"category\" : \"Generic\"\n",
    "}\n",
    "\n",
    "import bpy\n",
    " \n",
    "def register():\n",
    "    bpy.utils.register_class(ContextExecOperator)\n",
    "def unregister():\n",
    "    bpy.utils.unregister_class(ContextExecOperator) \n",
    "    \n",
    "    \n",
    "class ContextExecOperator(bpy.types.Operator):\n",
    "    bl_idname = \"sky.context_exec\"\n",
    "    bl_label = \"context_exec\"  \n",
    "    bl_options = {\"REGISTER\" }\n",
    "    \n",
    "    mFloat:bpy.props.FloatProperty(name=\"float\",default = 1.0)\n",
    "    mStr:bpy.props.StringProperty(name=\"string\",default = \"hello\")\n",
    "\n",
    "    #classmethod\n",
    "    def poll(cls,context):\n",
    "        return True\n",
    "    \n",
    "    def execute(self,context):\n",
    "        print(\"execute\")\n",
    "        return {'FINISHED'}\n",
    "    \n",
    "    def invoke (self,context,event):\n",
    "        print(\"invoke\")\n",
    "        return self.execute(context)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1c9d26a7",
   "metadata": {},
   "outputs": [],
   "source": [
    "#进入blender运行\n",
    "#可以按格式修改\n",
    "bpy.ops.sky.context_exec(mFloat = 2.0,mStr = 'hello!!')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e32b4ff",
   "metadata": {},
   "source": [
    "若有自定义的方法需要使用到context  则需要用context属性  而不是内部获得一个bpy.context"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c12cb944",
   "metadata": {},
   "outputs": [],
   "source": [
    "def run(self,context):\n",
    "    ..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "352eba79",
   "metadata": {},
   "source": [
    "# 九、Dialog弹窗"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7a9eb9c3",
   "metadata": {},
   "source": [
    "主要三大类：   \n",
    "- invoke_props_dialog   \n",
    "- invoke_props_popup\n",
    "- invoke_popup"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "be292506",
   "metadata": {},
   "outputs": [],
   "source": [
    "# DialogBox需要在invoke内定义\n",
    "def invoke(self,context,event):\n",
    "    wm = context.window_manager \n",
    "    return wm.invoke_props_dialog(self)\n",
    "\n",
    "#blender运行后弹窗  内有自定义的RNA属性  点OK会自动执行execute操作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "127ea683",
   "metadata": {},
   "outputs": [],
   "source": [
    "#简单绘制界面\n",
    "def draw(self,context):\n",
    "    layout = self.layout\n",
    "    row = layout.row()\n",
    "    row.prop(self,\"mFloat\",icon=\"BLENDER\")   # 图标名称可使用blender内置addon查看"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "28f9cbb0",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
